home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / BaseClasses / source.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  12.8 KB  |  520 lines

  1. //------------------------------------------------------------------------------
  2. // File: Source.cpp
  3. //
  4. // Desc: DirectShow  base classes - implements CSource, which is a Quartz
  5. //       source filter 'template.'
  6. //
  7. // Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
  8. //------------------------------------------------------------------------------
  9.  
  10.  
  11. // Locking Strategy.
  12. //
  13. // Hold the filter critical section (m_pFilter->pStateLock()) to serialise
  14. // access to functions. Note that, in general, this lock may be held
  15. // by a function when the worker thread may want to hold it. Therefore
  16. // if you wish to access shared state from the worker thread you will
  17. // need to add another critical section object. The execption is during
  18. // the threads processing loop, when it is safe to get the filter critical
  19. // section from within FillBuffer().
  20.  
  21. #include <streams.h>
  22.  
  23.  
  24. //
  25. // CSource::Constructor
  26. //
  27. // Initialise the pin count for the filter. The user will create the pins in
  28. // the derived class.
  29. CSource::CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid)
  30.     : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
  31.       m_iPins(0),
  32.       m_paStreams(NULL) {
  33. }
  34.  
  35. CSource::CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr)
  36.     : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
  37.       m_iPins(0),
  38.       m_paStreams(NULL) {
  39.     UNREFERENCED_PARAMETER(phr);
  40. }
  41.  
  42. #ifdef UNICODE
  43. CSource::CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid)
  44.     : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
  45.       m_iPins(0),
  46.       m_paStreams(NULL) {
  47. }
  48.  
  49. CSource::CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr)
  50.     : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
  51.       m_iPins(0),
  52.       m_paStreams(NULL) {
  53.     UNREFERENCED_PARAMETER(phr);
  54. }
  55. #endif
  56.  
  57. //
  58. // CSource::Destructor
  59. //
  60. CSource::~CSource() {
  61.     /*  Free our pins and pin array */
  62.     while(m_iPins != 0) {
  63.         // deleting the pins causes them to be removed from the array...
  64.         delete m_paStreams[m_iPins - 1];
  65.     }
  66.  
  67.     ASSERT(m_paStreams == NULL);
  68. }
  69.  
  70.  
  71. //
  72. //  Add a new pin
  73. //
  74. HRESULT CSource::AddPin(CSourceStream *pStream) {
  75.     CAutoLock lock(&m_cStateLock);
  76.  
  77.     /*  Allocate space for this pin and the old ones */
  78.     CSourceStream **paStreams = new CSourceStream *[m_iPins + 1];
  79.     if(paStreams == NULL) {
  80.         return E_OUTOFMEMORY;
  81.     }
  82.     if(m_paStreams != NULL) {
  83.         CopyMemory((PVOID)paStreams, (PVOID)m_paStreams,
  84.             m_iPins * sizeof(m_paStreams[0]));
  85.         paStreams[m_iPins] = pStream;
  86.         delete [] m_paStreams;
  87.     }
  88.     m_paStreams = paStreams;
  89.     m_paStreams[m_iPins] = pStream;
  90.     m_iPins++;
  91.     return S_OK;
  92. }
  93.  
  94. //
  95. //  Remove a pin - pStream is NOT deleted
  96. //
  97. HRESULT CSource::RemovePin(CSourceStream *pStream) {
  98.     int i;
  99.     for(i = 0; i < m_iPins; i++) {
  100.         if(m_paStreams[i] == pStream) {
  101.             if(m_iPins == 1) {
  102.                 delete [] m_paStreams;
  103.                 m_paStreams = NULL;
  104.             }
  105.             else {
  106.                 /*  no need to reallocate */
  107.                 while(++i < m_iPins)
  108.                     m_paStreams[i - 1] = m_paStreams[i];
  109.             }
  110.             m_iPins--;
  111.             return S_OK;
  112.         }
  113.     }
  114.     return S_FALSE;
  115. }
  116.  
  117. //
  118. // FindPin
  119. //
  120. // Set *ppPin to the IPin* that has the id Id.
  121. // or to NULL if the Id cannot be matched.
  122. STDMETHODIMP CSource::FindPin(LPCWSTR Id, IPin **ppPin) {
  123.     CheckPointer(ppPin,E_POINTER);
  124.     ValidateReadWritePtr(ppPin,sizeof(IPin *));
  125.     // The -1 undoes the +1 in QueryId and ensures that totally invalid
  126.     // strings (for which WstrToInt delivers 0) give a deliver a NULL pin.
  127.     int i = WstrToInt(Id) -1;
  128.     *ppPin = GetPin(i);
  129.     if(*ppPin!=NULL) {
  130.         (*ppPin)->AddRef();
  131.         return NOERROR;
  132.     }
  133.     else {
  134.         return VFW_E_NOT_FOUND;
  135.     }
  136. }
  137.  
  138. //
  139. // FindPinNumber
  140. //
  141. // return the number of the pin with this IPin* or -1 if none
  142. int CSource::FindPinNumber(IPin *iPin) {
  143.     int i;
  144.     for(i=0; i<m_iPins; ++i) {
  145.         if((IPin *)(m_paStreams[i])==iPin) {
  146.             return i;
  147.         }
  148.     }
  149.     return -1;
  150. }
  151.  
  152. //
  153. // GetPinCount
  154. //
  155. // Returns the number of pins this filter has
  156. int CSource::GetPinCount(void) {
  157.  
  158.     CAutoLock lock(&m_cStateLock);
  159.     return m_iPins;
  160. }
  161.  
  162.  
  163. //
  164. // GetPin
  165. //
  166. // Return a non-addref'd pointer to pin n
  167. // needed by CBaseFilter
  168. CBasePin *CSource::GetPin(int n) {
  169.  
  170.     CAutoLock lock(&m_cStateLock);
  171.  
  172.     // n must be in the range 0..m_iPins-1
  173.     // if m_iPins>n  && n>=0 it follows that m_iPins>0
  174.     // which is what used to be checked (i.e. checking that we have a pin)
  175.     if((n >= 0) && (n < m_iPins)) {
  176.  
  177.         ASSERT(m_paStreams[n]);
  178.         return m_paStreams[n];
  179.     }
  180.     return NULL;
  181. }
  182.  
  183.  
  184. //
  185.  
  186.  
  187. // *
  188. // * --- CSourceStream ----
  189. // *
  190.  
  191. //
  192. // Set Id to point to a CoTaskMemAlloc'd
  193. STDMETHODIMP CSourceStream::QueryId(LPWSTR *Id) {
  194.     CheckPointer(Id,E_POINTER);
  195.     ValidateReadWritePtr(Id,sizeof(LPWSTR));
  196.  
  197.     // We give the pins id's which are 1,2,...
  198.     // FindPinNumber returns -1 for an invalid pin
  199.     int i = 1+ m_pFilter->FindPinNumber(this);
  200.     if(i<1) return VFW_E_NOT_FOUND;
  201.     *Id = (LPWSTR)CoTaskMemAlloc(8);
  202.     if(*Id==NULL) {
  203.         return E_OUTOFMEMORY;
  204.     }
  205.     IntToWstr(i, *Id);
  206.     return NOERROR;
  207. }
  208.  
  209.  
  210.  
  211. //
  212. // CSourceStream::Constructor
  213. //
  214. // increments the number of pins present on the filter
  215. CSourceStream::CSourceStream(
  216.     TCHAR *pObjectName,
  217.     HRESULT *phr,
  218.     CSource *ps,
  219.     LPCWSTR pPinName)
  220.     : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
  221.       m_pFilter(ps) {
  222.  
  223.     *phr = m_pFilter->AddPin(this);
  224. }
  225.  
  226. #ifdef UNICODE
  227. CSourceStream::CSourceStream(
  228.     char *pObjectName,
  229.     HRESULT *phr,
  230.     CSource *ps,
  231.     LPCWSTR pPinName)
  232.     : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
  233.       m_pFilter(ps) {
  234.  
  235.     *phr = m_pFilter->AddPin(this);
  236. }
  237. #endif
  238. //
  239. // CSourceStream::Destructor
  240. //
  241. // Decrements the number of pins on this filter
  242. CSourceStream::~CSourceStream(void) {
  243.  
  244.     m_pFilter->RemovePin(this);
  245. }
  246.  
  247.  
  248. //
  249. // CheckMediaType
  250. //
  251. // Do we support this type? Provides the default support for 1 type.
  252. HRESULT CSourceStream::CheckMediaType(const CMediaType *pMediaType) {
  253.  
  254.     CAutoLock lock(m_pFilter->pStateLock());
  255.  
  256.     CMediaType mt;
  257.     GetMediaType(&mt);
  258.  
  259.     if(mt == *pMediaType) {
  260.         return NOERROR;
  261.     }
  262.  
  263.     return E_FAIL;
  264. }
  265.  
  266.  
  267. //
  268. // GetMediaType/3
  269. //
  270. // By default we support only one type
  271. // iPosition indexes are 0-n
  272. HRESULT CSourceStream::GetMediaType(int iPosition, CMediaType *pMediaType) {
  273.  
  274.     CAutoLock lock(m_pFilter->pStateLock());
  275.  
  276.     if(iPosition<0) {
  277.         return E_INVALIDARG;
  278.     }
  279.     if(iPosition>0) {
  280.         return VFW_S_NO_MORE_ITEMS;
  281.     }
  282.     return GetMediaType(pMediaType);
  283. }
  284.  
  285.  
  286. //
  287. // Active
  288. //
  289. // The pin is active - start up the worker thread
  290. HRESULT CSourceStream::Active(void) {
  291.  
  292.     CAutoLock lock(m_pFilter->pStateLock());
  293.  
  294.     HRESULT hr;
  295.  
  296.     if(m_pFilter->IsActive()) {
  297.         return S_FALSE; // succeeded, but did not allocate resources (they already exist...)
  298.     }
  299.  
  300.     // do nothing if not connected - its ok not to connect to
  301.     // all pins of a source filter
  302.     if(!IsConnected()) {
  303.         return NOERROR;
  304.     }
  305.  
  306.     hr = CBaseOutputPin::Active();
  307.     if(FAILED(hr)) {
  308.         return hr;
  309.     }
  310.  
  311.     ASSERT(!ThreadExists());
  312.  
  313.     // start the thread
  314.     if(!Create()) {
  315.         return E_FAIL;
  316.     }
  317.  
  318.     // Tell thread to initialize. If OnThreadCreate Fails, so does this.
  319.     hr = Init();
  320.     if(FAILED(hr))
  321.         return hr;
  322.  
  323.     return Pause();
  324. }
  325.  
  326.  
  327. //
  328. // Inactive
  329. //
  330. // Pin is inactive - shut down the worker thread
  331. // Waits for the worker to exit before returning.
  332. HRESULT CSourceStream::Inactive(void) {
  333.  
  334.     CAutoLock lock(m_pFilter->pStateLock());
  335.  
  336.     HRESULT hr;
  337.  
  338.     // do nothing if not connected - its ok not to connect to
  339.     // all pins of a source filter
  340.     if(!IsConnected()) {
  341.         return NOERROR;
  342.     }
  343.  
  344.     // !!! need to do this before trying to stop the thread, because
  345.     // we may be stuck waiting for our own allocator!!!
  346.  
  347.     hr = CBaseOutputPin::Inactive();  // call this first to Decommit the allocator
  348.     if(FAILED(hr)) {
  349.         return hr;
  350.     }
  351.  
  352.     if(ThreadExists()) {
  353.         hr = Stop();
  354.  
  355.         if(FAILED(hr)) {
  356.             return hr;
  357.         }
  358.  
  359.         hr = Exit();
  360.         if(FAILED(hr)) {
  361.             return hr;
  362.         }
  363.  
  364.         Close();    // Wait for the thread to exit, then tidy up.
  365.     }
  366.  
  367.     // hr = CBaseOutputPin::Inactive();  // call this first to Decommit the allocator
  368.     //if (FAILED(hr)) {
  369.     //  return hr;
  370.     //}
  371.  
  372.     return NOERROR;
  373. }
  374.  
  375.  
  376. //
  377. // ThreadProc
  378. //
  379. // When this returns the thread exits
  380. // Return codes > 0 indicate an error occured
  381. DWORD CSourceStream::ThreadProc(void) {
  382.  
  383.     HRESULT hr;  // the return code from calls
  384.     Command com;
  385.  
  386.     do {
  387.         com = GetRequest();
  388.         if(com != CMD_INIT) {
  389.             DbgLog((LOG_ERROR, 1, TEXT("Thread expected init command")));
  390.             Reply((DWORD) E_UNEXPECTED);
  391.         }
  392.     } while(com != CMD_INIT);
  393.  
  394.     DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread initializing")));
  395.  
  396.     hr = OnThreadCreate(); // perform set up tasks
  397.     if(FAILED(hr)) {
  398.         DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadCreate failed. Aborting thread.")));
  399.         OnThreadDestroy();
  400.         Reply(hr);  // send failed return code from OnThreadCreate
  401.         return 1;
  402.     }
  403.  
  404.     // Initialisation suceeded
  405.     Reply(NOERROR);
  406.  
  407.     Command cmd;
  408.     do {
  409.         cmd = GetRequest();
  410.  
  411.         switch(cmd) {
  412.  
  413.             case CMD_EXIT:
  414.                 Reply(NOERROR);
  415.                 break;
  416.  
  417.             case CMD_RUN:
  418.                 DbgLog((LOG_ERROR, 1, TEXT("CMD_RUN received before a CMD_PAUSE???")));
  419.                 // !!! fall through???
  420.  
  421.             case CMD_PAUSE:
  422.                 Reply(NOERROR);
  423.                 DoBufferProcessingLoop();
  424.                 break;
  425.  
  426.             case CMD_STOP:
  427.                 Reply(NOERROR);
  428.                 break;
  429.  
  430.             default:
  431.                 DbgLog((LOG_ERROR, 1, TEXT("Unknown command %d received!"), cmd));
  432.                 Reply((DWORD) E_NOTIMPL);
  433.                 break;
  434.         }
  435.     } while(cmd != CMD_EXIT);
  436.  
  437.     hr = OnThreadDestroy(); // tidy up.
  438.     if(FAILED(hr)) {
  439.         DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadDestroy failed. Exiting thread.")));
  440.         return 1;
  441.     }
  442.  
  443.     DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread exiting")));
  444.     return 0;
  445. }
  446.  
  447.  
  448. //
  449. // DoBufferProcessingLoop
  450. //
  451. // Grabs a buffer and calls the users processing function.
  452. // Overridable, so that different delivery styles can be catered for.
  453. HRESULT CSourceStream::DoBufferProcessingLoop(void) {
  454.  
  455.     Command com;
  456.  
  457.     OnThreadStartPlay();
  458.  
  459.     do {
  460.         while(!CheckRequest(&com)) {
  461.  
  462.             IMediaSample *pSample;
  463.  
  464.             HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,0);
  465.             if(FAILED(hr)) {
  466.                 Sleep(1);
  467.                 continue;   // go round again. Perhaps the error will go away
  468.                 // or the allocator is decommited & we will be asked to
  469.                 // exit soon.
  470.             }
  471.  
  472.             // Virtual function user will override.
  473.             hr = FillBuffer(pSample);
  474.  
  475.             if(hr == S_OK) {
  476.                 hr = Deliver(pSample);
  477.                 pSample->Release();
  478.  
  479.                 // downstream filter returns S_FALSE if it wants us to
  480.                 // stop or an error if it's reporting an error.
  481.                 if(hr != S_OK) {
  482.                     DbgLog((LOG_TRACE, 2, TEXT("Deliver() returned %08x; stopping"), hr));
  483.                     return S_OK;
  484.                 }
  485.  
  486.             }
  487.             else if(hr == S_FALSE) {
  488.                 // derived class wants us to stop pushing data
  489.                 pSample->Release();
  490.                 DeliverEndOfStream();
  491.                 return S_OK;
  492.             }
  493.             else {
  494.                 // derived class encountered an error
  495.                 pSample->Release();
  496.                 DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr));
  497.                 DeliverEndOfStream();
  498.                 m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0);
  499.                 return hr;
  500.             }
  501.  
  502.             // all paths release the sample
  503.         }
  504.  
  505.         // For all commands sent to us there must be a Reply call!
  506.  
  507.         if(com == CMD_RUN || com == CMD_PAUSE) {
  508.             Reply(NOERROR);
  509.         }
  510.         else if(com != CMD_STOP) {
  511.             Reply((DWORD) E_UNEXPECTED);
  512.             DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!")));
  513.         }
  514.     } while(com != CMD_STOP);
  515.  
  516.     return S_FALSE;
  517. }
  518.  
  519.  
  520.